package com.synthefact.daj.model.threaded.impl;

import java.util.HashSet;
import java.util.Set;

import com.synthefact.daj.model.ChannelIf;
import com.synthefact.daj.model.NetworkIf;
import com.synthefact.daj.model.NodeIf;
import com.synthefact.daj.model.threaded.ScheduleIf;
import com.synthefact.daj.model.threaded.ThreadedNodeIf;


/**
 * A network is a collection of nodes that interact.
 * <p>
 * Copyright (C) 2011 Synthefact
 * <p>
 * Based on code from (c) 1997, Wolfgang Schreiner <Wolfgang.Schreiner@risc.uni-linz.ac.at> 
 * http://www.risc.uni-linz.ac.at/software/daj
 */
public class Network extends Thread implements NetworkIf {

    private Set<NodeIf> iNodes = new HashSet<NodeIf>();
    private ScheduleIf scheduler = new Scheduler();

    @Override
    public Set<NodeIf> getNodes() {
	return iNodes;
    }

    @Override
    public int getNodeCount() {
	return iNodes.size();
    }

    @Override
    public void addNode(NodeIf node) {

	if (node == null) {
	    throw new IllegalArgumentException("node can't be null");
	}
	if (!(node instanceof Node)) {
	    throw new IllegalStateException(String.format("node: %s is not a threaded impl node", node));
	}

	iNodes.add(node);
	// B/c this is in the threaded.impl package, this cast should not fail
	scheduler.register((ThreadedNodeIf) node);
    }

    /**
     * Run the network. Start each node and hand off to the scheduler.
     */
    public synchronized void run() {
	scheduler.main(Thread.currentThread());
	for (NodeIf node : iNodes) {
	    // This network works only with threaded node implementations
	    // and so we must cast
	    ((Node) node).start();
	    ((Node) node).setActive(true);
	    Thread.yield();
	}
	scheduler.schedule();
    }

    @Override
    public void terminate(NodeIf node) {
	scheduler.terminate();
    }

    @Override
    public void fullyConnect() {
	for (NodeIf sendingNode: iNodes) {
	    for (NodeIf rcvNode: iNodes) {
		/*
		 * If the nodes are not equal then create a channel between
		 * them. Note that node equality is object equality.
		 */
		if (!sendingNode.equals(rcvNode)) {
		    ChannelIf channel = new Channel(sendingNode, rcvNode);
		    sendingNode.addOutChannel(channel);
		    rcvNode.addInChannel(channel);
		}
	    }
	}
	
    }

}
